/*
* Copyright (C) 2010-2012 Magnus Skjegstad
* 
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
* 
* The latest version of this library can be downloaded from http://mist-pubsub.googlecode.com
* 
*/
package com.skjegstad.mist2.datatypes;

import com.skjegstad.mist2.exceptions.MistException;

/**
 *
 * @author Magnus Skjegstad
 */
public class DataFragment extends DataElement {

    private int fragmentNo = 0;
    private boolean lastFragment = false;
    private Long memoizedHash = null;
    private Long completeHash = null;

    public DataFragment(NodeIdentifier sourceNode, long seqNr, byte[] data, TopicIdentifier topic, long age,
            long expireTime,
            int fragmentNo,
            boolean lastFragment, 
            long completeHash) {
        super(sourceNode, seqNr, data, topic, age, expireTime);
        this.fragmentNo = fragmentNo;
        this.lastFragment = lastFragment;
        this.completeHash = completeHash;
    }

    /**
     * Create new fragment from data element.
     *
     * @param dataElement data element the fragment belongs to.
     * @param fragmentSize the size of the fragment. This should be equal for all fragments (except possibly the last one)
     * @param fragmentNo the fragment number. First fragment is 0.
     * @throws MistException thrown if the parametres were invalid.
     */
    public DataFragment(DataElement dataElement, int fragmentSize, int fragmentNo) throws MistException {
        this(dataElement.getSourceNode(),
                dataElement.getSeqNr(),
                null,
                dataElement.getTopic(),
                dataElement.getAge(),
                dataElement.getExpireTime(),
                fragmentNo,
                false, 
                dataElement.getHash());

        if (fragmentNo * fragmentSize > dataElement.getPayloadLen())
            throw new MistException("Unable to create fragment from data element. Fragment number to large (#" + fragmentNo + ", size " + fragmentSize + ")");

        int start = fragmentSize * fragmentNo;
        int end = fragmentSize * (fragmentNo + 1);
        if (end > dataElement.getPayloadLen())
            end = dataElement.getPayloadLen();

        byte[] data = new byte[end-start];
        System.arraycopy(dataElement.getPayload(), start, data, 0, end-start);

        this.lastFragment = end == dataElement.getPayloadLen();
        this.payload = data;

    }

    public int getFragmentNo() {
        return fragmentNo;
    }

    public boolean isLastFragment() {
        return lastFragment;
    }

    @Override
    /**
     * Get hash of this fragment
     * 
     * @return fragment's hash
     */
    public long getHash() {
        if (memoizedHash == null)
            memoizedHash = DataElement.getHash(this.getSourceNode(), this.getSeqNr(), this.fragmentNo, this.getPayload(), 0, this.getPayload().length);
        return memoizedHash;
    }

    /**
     * Get the hash of the full data element when all fragments are assembled.
     * 
     * @return complete data element's hash
     */
    public long getCompleteHash() {
        return this.completeHash;
    }
    

}
